package net.zoneland.knowledge.service.impl;

import com.branchitech.commons.time.biz.IComplexWorkDayHandler;
import com.branchitech.commons.time.domain.HolidayQueryParam;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import net.zoneland.knowledge.config.TalkConfig;
import net.zoneland.knowledge.constant.BizConstant;
import net.zoneland.knowledge.constant.Constants;
import net.zoneland.knowledge.dao.CheckSyncSftpMapper;
import net.zoneland.knowledge.dao.UrgeHistoryMapper;
import net.zoneland.knowledge.dao.WorkApplyInfoMapper;
import net.zoneland.knowledge.enums.SyncTypeEnum;
import net.zoneland.knowledge.enums.UrgeTypeEnum;
import net.zoneland.knowledge.model.LoginUserPojo;
import net.zoneland.knowledge.model.UrgeHistory;
import net.zoneland.knowledge.model.WorkApplyInfo;
import net.zoneland.knowledge.model.WorkSubTaskInfo;
import net.zoneland.knowledge.service.EmailService;
import net.zoneland.knowledge.service.NotifyService;
import net.zoneland.knowledge.service.ScheduleService;
import net.zoneland.knowledge.service.UniFlowService;
import net.zoneland.knowledge.service.bean.ResultInfo;
import net.zoneland.knowledge.utils.WorkApplyOperationUtils;
import net.zoneland.uniflow.client.TaskObject;
import net.zoneland.uniflow.client.UserInfo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
public class ScheduleServiceImpl implements ScheduleService {

    private static final Logger LOG = LoggerFactory.getLogger(ScheduleServiceImpl.class);

    @Autowired
    private IComplexWorkDayHandler workDayHandler;

    @Autowired
    private WorkApplyInfoMapper workApplyInfoMapper;

    @Autowired
    private NotifyService notifyService;

    @Autowired
    private EmailService<TaskObject[], WorkApplyInfo> emailService;

    /**
     * 流程业务接口.
     */
    @Autowired
    private UniFlowService uniFlowService;

    @Autowired
    private TalkConfig talkConfig;

    @Autowired
    private UrgeHistoryMapper urgeHistoryMapper;

    @Autowired
    private CheckSyncSftpMapper checkSyncSftpMapper;

    /**
     * 地址.
     */
    @Value("${sync.checkUser:fangye4}")
    private String syncCheckUser;

    /**
     * 下一个工作日
     */
    private static final int lastDay = 1;

    /**
     * 时间转化格式.
     */
    private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");

    /**
     * 增量的类型，目前是党费和三会一课.
     */
    private static final List<String> INCREMENT_LIST = ImmutableList.of(SyncTypeEnum.SHYK.toString(), SyncTypeEnum.DF.toString());

    /**
     * 同步标题。
     */
    private static final String SYNC_TITLE = "党建sftp文件传输告警。";

    /**
     * sftp增量文件没导入告警内容后缀。
     */
    private static final String NOT_IMPORT_STRING = " 缺失<br/>";

    /**
     * sftp全量文件连续三天没有导入告警内容后缀。
     */
    private static final String THREE_DAYS_NOT_IMPORT_STRING = " 连续三天缺失<br/>";

    @Override
    @Scheduled(cron = "1 0 8 * * ?")
    @SchedulerLock(name = "workApplyInfoWarning",lockAtMostFor = "1H",lockAtLeastFor ="30S")
    public void workApplyInfoWarning() {
        LOG.info("workApplyInfoWarning start");

        //只有工作日才会发催办邮件
        if (workDayHandler.isWorkDay(new HolidayQueryParam(), new Date())){
            return;
        }

        final long workDay = workDayHandler.afterWorkDays(new HolidayQueryParam(), lastDay);

        //获取需要催办的数据，状态是RESPONDER_APPROVE,SUB_ORG_USER_CONFIRM,SUB_ORG_USER_HANDLE
        final List<WorkApplyInfo> warningUsers = workApplyInfoMapper.getWarningUserByTime(new Date(), new Date(workDay));
        warningUsers.forEach(info->{
            final ResultInfo resultInfo = uniFlowService.queryTasksByApplyId(BizConstant.WISDOM_SUB_FLOW_PROCESS_CODE, info.getApplyId());
            emailService.sendEmail(this::sendEmailFunction, (TaskObject[])resultInfo.getData(), info);
        });
        LOG.info("workApplyInfoWarning end");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean manualUrge(final List<String> subTaskIds, final LoginUserPojo user) {
        final Date now = new Date();
        final int totalUrgeCount = subTaskIds.stream()
                .mapToInt(subTaskId -> doManualUrge(subTaskId, user, now))
                .sum();
        return totalUrgeCount > 0;
    }

    @Override
    @Scheduled(cron = "0 0 1 * * ?")
    @SchedulerLock(name = "checkSyncSftp",lockAtMostFor = "1H",lockAtLeastFor ="30S")
    public void checkSyncSftp() {
        LOG.info("checkSyncSftp start.");
        final String lastThreeDaysTime = LocalDate.now().plusDays(-3).format(dateTimeFormatter);

        //全量的数据三天内有一天有即可，否则告警
        final List<String> allSftpRecord = checkSyncSftpMapper.getSftpRecordByTimeForAllSync(lastThreeDaysTime);
        final List<String> syncTypeList = allSftpRecord.stream()
                .filter(syncType -> !INCREMENT_LIST.contains(syncType))
                .collect(Collectors.toList());

        //增量的数据如果大前天的数据没有
        final List<String> incrementTypeForNotImport = checkSyncSftpMapper.getIncrementTypeForNotImport(lastThreeDaysTime);

        //只要两个list有一个有内容，就需要发告警邮件。
        if (CollectionUtils.isNotEmpty(syncTypeList) || CollectionUtils.isNotEmpty(incrementTypeForNotImport)) {
            final StringBuilder stringBuilder = new StringBuilder();
            syncTypeList.forEach(type -> {
                stringBuilder.append(SyncTypeEnum.getNameByType(type)).append(" ").append(lastThreeDaysTime).append(NOT_IMPORT_STRING);
            });
            incrementTypeForNotImport.forEach(type -> {
                stringBuilder.append(SyncTypeEnum.getNameByType(type)).append(THREE_DAYS_NOT_IMPORT_STRING);
            });
            LOG.info("checkSyncSftp sendEmail,content:[{}]", stringBuilder);
            notifyService.notifyByEmail(StringUtils.split(syncCheckUser, Constants.STRING_SPLIT), SYNC_TITLE, stringBuilder.toString());
        }
        LOG.info("checkSyncSftp end.");
    }

    /**
     *
     * @param subTaskId 分任务id
     * @param user 当前用户
     * @param now 当前时间
     * @return 催办次数
     */
    private int doManualUrge(final String subTaskId, final LoginUserPojo user, final Date now) {

        final WorkSubTaskInfo workSubTaskInfo = workApplyInfoMapper.getWorkSubTaskInfoById(subTaskId);
        final WorkApplyInfo workApplyInfo = workApplyInfoMapper.findWorkApplyInfoById(workSubTaskInfo.getWorkApplyId());

        //判断权限
        Preconditions.checkArgument(WorkApplyOperationUtils.haveOperatePermission(workApplyInfo, user), "您没有催办的权限！");

        final AtomicInteger count = new AtomicInteger(0);
        workApplyInfoMapper.getWorkSubTaskInfoByParentTaskId(subTaskId).stream()
                // 排除不需要催办的分任务，（已完成的状态，已上报和完成）
                .filter(subTask -> !WorkSubTaskHandleServiceImpl.FINISH_NODE_FOR_HANDLER.contains(subTask.getNode()))
                // 获取工单id
                .map(WorkSubTaskInfo::getWorkApplyId)
                // 工单id去重
                .distinct()
                // 根据工单id要查询两个东西：待办和工单信息
                .map(applyId -> {
                    final WorkApplyInfo subApplyInfo = workApplyInfoMapper.findWorkApplyInfoById(applyId);
                    return Pair.of(
                            getTaskObjectExcludeCurrentUser(subApplyInfo.getApplyId(), user),
                            subApplyInfo);
                })
                // 遍历数据，实际当前用户也有可能待办，需要过滤
                //过滤没有待办的数据
                .filter(pair -> ArrayUtils.isNotEmpty(pair.getLeft()))
                .forEach(pair -> {
                    // 记录催办次数
                    count.incrementAndGet();
                    // 发催办邮件
                    emailService.sendEmail(this::sendEmailFunction, pair.getLeft(), pair.getRight());
                    // 保存催办日志
                    saveUrgeHistory(workSubTaskInfo, pair.getLeft(), user, now, UrgeTypeEnum.MANUAL);
                });

        return count.get();
    }

    private void saveUrgeHistory(final WorkSubTaskInfo workSubTaskInfo, final TaskObject[] taskObjects,
                                 final LoginUserPojo user, final Date now, final UrgeTypeEnum urgeTypeEnum) {

        for (TaskObject taskObject : taskObjects) {
            UrgeHistory history = new UrgeHistory();
            history.setId(UUID.randomUUID().toString());
            history.setTiid(taskObject.getTiid());
            final UserInfo handler = taskObject.getHandler();
            history.setUrgeUid(handler.getUserId());
            history.setUrgeName(handler.getUserName());
            history.setSubTaskId(workSubTaskInfo.getId());
            history.setSubTaskName(workSubTaskInfo.getName());
            history.setWorkApplyId(workSubTaskInfo.getWorkApplyId());
            history.setCreatorUid(user.getUserID());
            history.setUrgeType(urgeTypeEnum);
            history.setCreateTime(now);
            urgeHistoryMapper.insert(history);
        }

    }
    /**
     * 根据工单id获取排除当前用户的待办.
     * @param applyId 工单id
     * @param user 当前用户
     * @return
     */
    private TaskObject[] getTaskObjectExcludeCurrentUser(final String applyId, final LoginUserPojo user) {
        final ResultInfo resultInfo = uniFlowService.queryTasksByApplyId(BizConstant.WISDOM_SUB_FLOW_PROCESS_CODE, applyId);
        // 实际当前用户也有可能待办，需要过滤
        return Arrays.stream(((TaskObject[]) resultInfo.getData()))
                .filter(taskObject -> !StringUtils.equals(taskObject.getHandler().getUserId(), user.getUserID()))
                .toArray(TaskObject[]::new);
    }
    /**
     * 邮件发送
     */
    private void sendEmailFunction(Map<String,Object> map){
        String title = "党建机器人-任务派发流程：{0}(催办)";
        final String contentTemplate = "<a href=\"{0}\">请处理党建机器人-任务派发流程任务工单：{1}</a>"
                + "<br/>发起人：{2}"
                + "<br/>发起时间：{3}";
        emailService.sendEmailFunction(title,contentTemplate,BizConstant.WISDOM_MAIN_PROCESS_CODE,talkConfig.getSiteUrl(),map);
    }
    
}
